/*
 * Copyright (C) 2006-2007 Eskil Bylund
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#if ENABLE_NOTIFICATIONS

using System;
using System.Collections.Generic;
using System.IO;

using Gtk;
using Mono.Unix;
using Notifications;

using DCSharp.Backend.Connections;
using DCSharp.Backend.Managers;
using DCSharp.Settings;

namespace DCSharp.Gui
{
	public class Notifications
	{
		private const int MaxMessageLength = 64;

		private Runtime runtime;
		private Gui gui;
		private NotificationSettings settings;

		private Dictionary<User, Notification> chatNotifications;
		private Notification downloadComplete;

		public Notifications(Runtime runtime, Gui gui,
			NotificationSettings settings)
		{
			if (runtime == null)
			{
				throw new ArgumentNullException("runtime");
			}
			if (gui == null)
			{
				throw new ArgumentNullException("gui");
			}
			if (settings == null)
			{
				throw new ArgumentNullException("settings");
			}
			this.runtime = runtime;
			this.gui = gui;
			this.settings = settings;

			chatNotifications = new Dictionary<User, Notification>();

			downloadComplete = new Notification();
			downloadComplete.Category = "transfer.complete";
			downloadComplete.AddHint("desktop-entry", "dcsharp");

			// Events
			runtime.DownloadManager.DownloaderRemoved += OnDownloaderRemoved;
			runtime.HubManager.HubConnected += OnHubConnected;
		}

		#region Methods

		#region Chat

		private void OnHubConnected(object obj, ConnectionEventArgs args)
		{
			HubConnection hub = (HubConnection)args.Connection;

			hub.PrivateMessage += OnPrivateMessage;
			hub.StateChanged += OnHubStateChanged;
		}

		private void OnPrivateMessage(object obj, MessageEventArgs args)
		{
			HubConnection hub = (HubConnection)obj; 
			if (!settings.PrivateChat || args.To != hub.LocalIdentity)
			{
				// Unwanted notification, or not a message to me...
				return;
			}

			Application.Invoke(delegate
			{
				Gdk.WindowState state = gui.MainWindow.GdkWindow.State;

				ChatPage page = gui.MainWindow.Current as ChatPage;
				if ((page != null && page.RemoteUser == args.From.User) &&
					((state & Gdk.WindowState.Iconified) != Gdk.WindowState.Iconified))
				{
					// The chat page with this user is visible
					return;
				}

				// Find an existing notification
				Notification notification;
				if (!chatNotifications.TryGetValue(args.From.User, out notification))
				{
					string summary = GLib.Markup.EscapeText(String.Format(
						Catalog.GetString("New Message from {0}"), args.From.Nick));

					notification = new Notification(summary, String.Empty);
					notification.Category = "im";
					notification.Closed += delegate
					{
						chatNotifications.Remove(args.From.User);
					};
				}
				else
				{
					notification.Close();
				}

				// Create the message
				string message = args.Message;
				if (message.Length > MaxMessageLength)
				{
					message = String.Format("{0}...",
						message.Substring(0, MaxMessageLength).TrimEnd(' '));
				}
				message = String.Format("\"{0}\"", message);

				// Initialize the notification
				notification.Body = GLib.Markup.EscapeText(message);;
				notification.ClearActions();
				notification.AddAction("respond", Catalog.GetString("Show"), delegate
				{
					gui.ShowChat(args.To.User, args.From.User, true);
					gui.MainWindow.Show();
				});
				chatNotifications[args.From.User] = notification;

				TryShowNotification(notification);
			});
		}

		private void OnHubStateChanged(object obj, EventArgs args)
		{
			HubConnection hub = (HubConnection)obj;

			if (hub.State == ConnectionState.Disconnected)
			{
				hub.PrivateMessage -= OnPrivateMessage;
				hub.StateChanged -= OnHubStateChanged;
			}
		}

		#endregion

		#region Download

		private void OnDownloaderRemoved(object obj, DownloadEventArgs args)
		{
			FileDownloader downloader = args.Downloader as FileDownloader;
			if (downloader == null || downloader.Progress < 1)
			{
				return;
			}

			if (settings.AllDownloadsComplete && AllFilesComplete())
			{
				ShowAllDownloadsComplete();
			}
			else if (settings.DownloadComplete)
			{
				ShowDownloadComplete(downloader);
			}
		}

		private bool AllFilesComplete()
		{
			if (runtime.DownloadManager.Count == 0)
			{
				return true;
			}
			foreach (Downloader d in runtime.DownloadManager)
			{
				if (d is FileDownloader)
				{
					return false;
				}
			}
			return true;
		}

		private void ShowAllDownloadsComplete()
		{
			Application.Invoke(delegate
			{
				Notification notification = downloadComplete;

				notification.Close();

				notification.Summary = Catalog.GetString("Downloads complete");
				notification.Body = Catalog.GetString("All files have finished downloading.");
				notification.IconName = Stock.Network;

				// Actions
				notification.ClearActions();
				notification.AddAction("default", String.Empty, delegate
				{
					gui.ShowDownloads(true);
					gui.MainWindow.Show();
				});

				TryShowNotification(notification);
			});
		}

		private void ShowDownloadComplete(FileDownloader downloader)
		{
			Application.Invoke(delegate
			{
				RemoteFileInfo remoteFile = downloader.RemoteFile;
				Notification notification = downloadComplete;

				notification.Close();

				notification.Summary = Catalog.GetString("Download complete");
				notification.Body = GLib.Markup.EscapeText(remoteFile.Name);
				notification.IconName = Util.GetIconName(remoteFile.Name);

				// Actions
				notification.ClearActions();
				notification.AddAction("default", String.Empty, delegate
				{
					gui.ShowDownloads(true);
					gui.MainWindow.Show();
				});
				notification.AddAction("open", Catalog.GetString("Open"), delegate
				{
					Util.OpenFile(Path.Combine(downloader.SavePath, remoteFile.Name));
				});
				notification.AddAction("browse", Catalog.GetString("Open Folder"), delegate
				{
					Util.OpenFile(downloader.SavePath);
				});
				TryShowNotification(notification);
			});
		}

		private static void TryShowNotification(Notification notification)
		{
			try
			{
				notification.Show();
			}
			catch (Exception e)
			{
				Console.Error.WriteLine("Failed to show notification: {0}", e);
			}
		}

		#endregion

		#endregion
	}
}

#endif
